package com.jack.index;

import com.jack.entity.User;
import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.analysis.standard.StandardAnalyzer;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field;
import org.apache.lucene.document.FieldType;
import org.apache.lucene.document.TextField;
import org.apache.lucene.index.DirectoryReader;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.index.IndexWriterConfig;
import org.apache.lucene.queryparser.classic.QueryParser;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.ScoreDoc;
import org.apache.lucene.search.TopDocs;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.FSDirectory;

import java.io.File;
import java.io.FileReader;
import java.nio.file.Paths;

/**
 * Created by zhang_j on 2019/10/30
 * 使用Lucene来查询出站内的数据，首先我们需要有个索引库。
 * 于是我们先创建索引库，将我们的数据存到索引库中
 * 然后通过findIndexDB查询数据库内容
 */
public class CreateIndexDB {

    public static void main(String[] args) throws Exception{
//        createIndexDB();
//        findIndexDB();
//        index();
        search("D:\\lucene\\createIndexDB","test");
    }
    //从磁盘上读取文件
    public static void index() throws Exception{
        Directory directory = FSDirectory.open(Paths.get("D:\\lucene\\createIndexDB"));
        IndexWriterConfig config = new IndexWriterConfig();
        config.setMaxBufferedDocs(10);

        IndexWriter indexWriter = new IndexWriter(directory, config);
        File[] files = new File("D:\\lucene\\data").listFiles();
        for(File f:files){
            //输出索引文件的路径
            System.out.println("索引文件：" + f.getCanonicalPath());
            //获取文档，文档里再设置每个字段
            Document doc = getDocument(f);
            //开始写入,就是把文档写进了索引文件里去了；
            indexWriter.addDocument(doc);
        }

        indexWriter.close();
    }

    //从刚刚磁盘上读取文件生成的索引库读取
    public static void search(String indexDir, String q) throws Exception {

        // 得到读取索引文件的路径
        Directory dir = FSDirectory.open(Paths.get(indexDir));
        // 通过dir得到的路径下的所有的文件
        IndexReader reader = DirectoryReader.open(dir);
        // 建立索引查询器
        IndexSearcher is = new IndexSearcher(reader);
        // 实例化分析器
        Analyzer analyzer = new StandardAnalyzer();
        // 建立查询解析器
        /**
         * 第一个参数是要查询的字段； 第二个参数是分析器Analyzer
         */
        QueryParser parser = new QueryParser("contents", analyzer);
        // 根据传进来的p查找
        Query query = parser.parse(q);
        // 计算索引开始时间
        long start = System.currentTimeMillis();
        // 开始查询
        /**
         * 第一个参数是通过传过来的参数来查找得到的query； 第二个参数是要出查询的行数
         */
        TopDocs hits = is.search(query, 10);
        // 计算索引结束时间
        long end = System.currentTimeMillis();
        System.out.println("匹配 " + q + " ，总共花费" + (end - start) + "毫秒" + "查询到" + hits.totalHits + "个记录");
        // 遍历hits.scoreDocs，得到scoreDoc
        /**
         * ScoreDoc:得分文档,即得到文档 scoreDocs:代表的是topDocs这个文档数组
         *
         * @throws Exception
         */
        for (ScoreDoc scoreDoc : hits.scoreDocs) {
            Document doc = is.doc(scoreDoc.doc);
            System.out.println(doc.get("fullPath"));
        }

        // 关闭reader
        reader.close();
    }

    /**
     * 获取文档，文档里再设置每个字段
     *
     * @param f
     * @return document
     */
    private static Document getDocument(File f) throws Exception {
        Document doc = new Document();
        //把设置好的索引加到Document里，以便在确定被索引文档
        doc.add(new TextField("contents", new FileReader(f)));
        //Field.Store.YES：把文件名存索引文件里，为NO就说明不需要加到索引文件里去
        doc.add(new TextField("fileName", f.getName(), Field.Store.YES));
        //把完整路径存在索引文件里
        doc.add(new TextField("fullPath", f.getCanonicalPath(), Field.Store.YES));
        return doc;
    }

    /**
     * 创建索引库的步骤：
     *      1）创建JavaBean的对象
     *      2）创建Document对象
     *      3）将所有JavaBean对象的所有属性值，均放到Document对象中去，属性名可以和JavaBean相同或不同
     *      4）创建IndexWriter对象
     *      5）将Document对象通过IndexWriter对象写入索引库中
     *      6）关闭IndexWriter对象
     */
    public static void createIndexDB() throws Exception{
        //把数据填充到JavaBean对象中
        User user = new User("1","张三","优秀的程序员");

        //创建Document对象【导入的是Lucene包下的Document对象】
        Document document = new Document();

        //将JavaBean对象所有的属性值，均放到Document对象中去，属性名可以和JavaBean相同或不同
        /**
         * 向Document对象加入一个字段
         * 参数一：字段的关键字
         * 参数二：字符的值
         * 参数三：FieldType记录了很多属性，其中默认将存储的数据拆分到词汇表中
         * 是否要存储到原始记录表中
         *      YES表示是
         *      NO表示否
         * 参数四：是否需要将存储的数据拆分到词汇表中
         *      ANALYZED表示拆分
         *      NOT_ANALYZED表示不拆分
         *
         * */
        FieldType fieldType = new FieldType();
        fieldType.setStored(true);
        document.add(new Field("id", user.getId(), fieldType));
        document.add(new Field("userName", user.getUserName(), fieldType));
        document.add(new Field("sal", user.getSal(), fieldType));

        //创建IndexWriter对象
        //目录指定为D:/createIndexDB
        Directory directory = FSDirectory.open(Paths.get("D:\\createIndexDB"));

        //使用标准的分词算法对原始记录表进行拆分
//        Analyzer analyzer = new StandardAnalyzer();
        //LIMITED默认是1W个
        IndexWriterConfig config = new IndexWriterConfig();
        config.setMaxBufferedDocs(10);

        /**
         * IndexWriter将我们的document对象写到硬盘中
         *
         * 参数一：Directory d,写到硬盘中的目录路径是什么
         * 参数二：Analyzer a, 以何种算法来对document中的原始记录表数据进行拆分成词汇表
         * 参数三：MaxFieldLength mfl 最多将文本拆分出多少个词汇
         *
         * */
        IndexWriter indexWriter = new IndexWriter(directory, config);

        //将Document对象通过IndexWriter对象写入索引库中
        indexWriter.addDocument(document);

        //关闭IndexWriter对象
        indexWriter.close();
    }

    /**
     * 根据关键字查询索引库中的内容：
     *      1）创建IndexSearcher对象
     *      2）创建QueryParser对象
     *      3）创建Query对象来封装关键字
     *      4）用IndexSearcher对象去索引库中查询符合条件的前100条记录，不足100条记录的以实际为准
     *      5）获取符合条件的编号
     *      6）用indexSearcher对象去索引库中查询编号对应的Document对象
     *      7）将Document对象中的所有属性取出，再封装会JavaBean对象中去，并加入到集合中保存，备用
     */
    public static void findIndexDB() throws Exception{
        //参数一：IndexSearcher(Directory path)查询以XXX目录的索引库
        Directory directory = FSDirectory.open(Paths.get("D:\\createIndexDB"));
        //创建IndexSearcher对象
        // 通过dir得到的路径下的所有的文件
        IndexReader reader = DirectoryReader.open(directory);
        IndexSearcher indexSearcher = new IndexSearcher(reader);

        //创建QueryParser对象
        /**
         * 参数一：String f,【要查询的字段】
         * 参数二：Analyzer a【使用的拆词算法】
         * */
        Analyzer analyzer = new StandardAnalyzer();
        QueryParser queryParser = new QueryParser( "userName", analyzer);

        //给出要查询的关键字
        String keyWords = "钟";

        //创建Query对象来封装关键字
        Query query = queryParser.parse(keyWords);

        //用IndexSearcher对象去索引库中查询符合条件的前100条记录，不足100条记录的以实际为准
        TopDocs topDocs = indexSearcher.search(query, 100);

        //获取符合条件的编号

        for (int i = 0; i < topDocs.scoreDocs.length; i++) {

            ScoreDoc scoreDoc = topDocs.scoreDocs[i];
            int no = scoreDoc.doc;
            //用indexSearcher对象去索引库中查询编号对应的Document对象
            Document document = indexSearcher.doc(no);

            //将Document对象中的所有属性取出，再封装回JavaBean对象中去
            String id = document.get("id");
            String userName = document.get("userName");
            String sal = document.get("sal");

            User user = new User(id, userName, sal);
            System.out.println(user);

        }
    }

}
